/**********************************************************************************
 * $URL: https://source.sakaiproject.org/svn/sam/trunk/samigo-app/src/java/org/sakaiproject/tool/assessment/ui/listener/author/ConfirmPublishAssessmentListener.java $
 * $Id: ConfirmPublishAssessmentListener.java 108255 2012-05-17 18:30:57Z ktsao@stanford.edu $
 ***********************************************************************************
 *
 * Copyright (c) 2004, 2005, 2006, 2007, 2008 The Sakai Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **********************************************************************************/



package org.sakaiproject.tool.assessment.ui.listener.author;

import java.util.ArrayList;
import java.util.Date;

import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService;
import org.sakaiproject.service.gradebook.shared.GradebookService;
import org.sakaiproject.spring.SpringBeanLocator;
import org.sakaiproject.tool.assessment.data.dao.assessment.AssessmentAccessControl;
import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentAccessControlIfc;
import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentMetaDataIfc;
import org.sakaiproject.tool.assessment.data.ifc.assessment.EvaluationModelIfc;
import org.sakaiproject.tool.assessment.facade.AgentFacade;
import org.sakaiproject.tool.assessment.facade.AssessmentFacade;
import org.sakaiproject.tool.assessment.integration.context.IntegrationContextFactory;
import org.sakaiproject.tool.assessment.integration.helper.ifc.GradebookServiceHelper;
import org.sakaiproject.tool.assessment.services.assessment.AssessmentService;
import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService;
import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentBean;
import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentSettingsBean;
import org.sakaiproject.tool.assessment.ui.bean.author.AuthorBean;
import org.sakaiproject.tool.assessment.ui.bean.author.PublishRepublishNotificationBean;
import org.sakaiproject.tool.assessment.ui.bean.authz.AuthorizationBean;
import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil;
import org.sakaiproject.tool.assessment.util.TextFormat;
import org.sakaiproject.util.FormattedText;

/**
 * <p>Title: Samigo</p>2
 * <p>Description: Sakai Assessment Manager</p>
 * @author Ed Smiley
 * @version $Id: ConfirmPublishAssessmentListener.java 108255 2012-05-17 18:30:57Z ktsao@stanford.edu $
 */

public class ConfirmPublishAssessmentListener
    implements ActionListener {

  private static Log log = LogFactory.getLog(ConfirmPublishAssessmentListener.class);
  //private static ContextUtil cu;
  private static final GradebookServiceHelper gbsHelper =
      IntegrationContextFactory.getInstance().getGradebookServiceHelper();
  private static final boolean integrated =
      IntegrationContextFactory.getInstance().isIntegrated();
  private boolean isFromActionSelect = false;

  public ConfirmPublishAssessmentListener() {
  }

  public void processAction(ActionEvent ae) throws AbortProcessingException {
    FacesContext context = FacesContext.getCurrentInstance();
    ExternalContext extContext = context.getExternalContext();
    AssessmentSettingsBean assessmentSettings = (AssessmentSettingsBean) ContextUtil.lookupBean("assessmentSettings");
    AssessmentBean assessmentBean = (AssessmentBean) ContextUtil.lookupBean("assessmentBean");
    AuthorBean author = (AuthorBean) ContextUtil.lookupBean("author");
    //#1 - permission checking before proceeding - daisyf
    String assessmentId=String.valueOf(assessmentSettings.getAssessmentId());
    SaveAssessmentSettings s = new SaveAssessmentSettings();
    AssessmentService assessmentService = new AssessmentService();
    AssessmentFacade assessment = assessmentService.getAssessment(
        assessmentId);
    if (!passAuthz(context, assessment.getCreatedBy())){
      assessmentSettings.setOutcomePublish("editAssessmentSettings");
      author.setIsErrorInSettings(true);
      return;
    }

    assessmentBean.setAssessment(assessment);
    
    // Only if the assessment already have question, this page will be excuted
    assessmentSettings.setHasQuestions(true);
    
    //proceed to look for error, save assessment setting and confirm publish
    //#2a - look for error: check if core assessment title is unique
    boolean error=false;

    String assessmentName = TextFormat.convertPlaintextToFormattedTextNoHighUnicode(log, assessmentSettings.getTitle());
    if(assessmentName!=null &&(assessmentName.trim()).equals("")){
     	String nameEmpty_err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","assessmentName_empty");
	context.addMessage(null,new FacesMessage(nameEmpty_err));
	error=true;
    }
    if(!assessmentService.assessmentTitleIsUnique(assessmentId,assessmentName,false)){
      String nameUnique_err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","assessmentName_error");
      context.addMessage(null,new FacesMessage(nameUnique_err));
      error=true;
    }
    
 // check if start date is valid
    if(!assessmentSettings.getIsValidStartDate()){
    	String startDateErr = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.GeneralMessages","invalid_start_date");
    	context.addMessage(null,new FacesMessage(startDateErr));
    	error=true;
    }
    // check if due date is valid
    if(!assessmentSettings.getIsValidDueDate()){
    	String dueDateErr = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.GeneralMessages","invalid_due_date");
    	context.addMessage(null,new FacesMessage(dueDateErr));
    	error=true;
    }
    // check if retract date is valid
    if(!assessmentSettings.getIsValidRetractDate()){
    	String retractDateErr = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.GeneralMessages","invalid_retrack_date");
    	context.addMessage(null,new FacesMessage(retractDateErr));
    	error=true;
    }
    

    Date startDate = assessmentSettings.getStartDate();
    Date dueDate = assessmentSettings.getDueDate();
    Date retractDate = assessmentSettings.getRetractDate();
    boolean isRetractEarlierThanAvaliable = false;
    if ((dueDate != null && startDate != null && dueDate.before(startDate)) ||
    	(dueDate != null && startDate == null && dueDate.before(new Date()))) {
    	String dateError1 = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","due_earlier_than_avaliable");
    	context.addMessage(null,new FacesMessage(FacesMessage.SEVERITY_WARN, dateError1, null));
    	error=true;
    	assessmentSettings.setStartDate(new Date());
    }
    if ((retractDate != null && startDate != null && retractDate.before(startDate)) ||
        (retractDate != null && startDate == null && retractDate.before(new Date()))) {
    	String dateError2 = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","retract_earlier_than_avaliable");
    	context.addMessage(null,new FacesMessage(FacesMessage.SEVERITY_WARN, dateError2, null));
    	error=true;
    	isRetractEarlierThanAvaliable = true;
    	assessmentSettings.setStartDate(new Date());
    }
    if (!isRetractEarlierThanAvaliable && (retractDate != null && dueDate != null && retractDate.before(dueDate))) {
    	String dateError3 = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","retract_earlier_than_due");
    	context.addMessage(null,new FacesMessage(FacesMessage.SEVERITY_WARN, dateError3, null));
    	error=true;
    }
    
    // SAM-1088
    // if late submissions not allowed and retract date is null, set retract date to due date
    if (assessmentSettings.getLateHandling() != null && AssessmentAccessControlIfc.NOT_ACCEPT_LATE_SUBMISSION.toString().equals(assessmentSettings.getLateHandling()) &&
    		retractDate == null && dueDate != null && assessmentSettings.getAutoSubmit()) {
    	retractDate = dueDate;
    	assessmentSettings.setRetractDate(dueDate);
    }
    // if auto-submit is enabled, make sure retract date is set
    if (assessmentSettings.getAutoSubmit() && retractDate == null) {
    	String autoSubmitEnabled = ServerConfigurationService.getString("samigo.autoSubmit.enabled");
  	  	if ("true".equalsIgnoreCase(autoSubmitEnabled)) {
  	  		String dateError4 = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","retract_required_with_auto_submit");
  	  		context.addMessage(null,new FacesMessage(FacesMessage.SEVERITY_WARN, dateError4, null));
  	  		error=true;
  	  	}
    }

    if (!isFromActionSelect) {
    	if (assessmentSettings.getReleaseTo().equals(AssessmentAccessControl.RELEASE_TO_SELECTED_GROUPS)) {
    		String[] groupsAuthorized = assessmentSettings.getGroupsAuthorizedToSave(); //getGroupsAuthorized();
    		if (groupsAuthorized == null || groupsAuthorized.length == 0) {
    			String releaseGroupError = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.GeneralMessages","choose_one_group");
    			context.addMessage(null,new FacesMessage(releaseGroupError));
    			error=true;
    			assessmentSettings.setNoGroupSelectedError(true);
    		}
    		else {
    			assessmentSettings.setNoGroupSelectedError(false);
    		}
    	}


    	//#2c - validate if this is a time assessment, is there a time entry?
    	Object time=assessmentSettings.getValueMap().get("hasTimeAssessment");
    	boolean isTime=false;
    	if (time!=null) {
    		// Because different flow might get different type, we test it before cast.
    		if (time instanceof java.lang.String) {
    			isTime = Boolean.getBoolean((String) time);
    		}
    		else if (time instanceof java.lang.Boolean) {
    			isTime=((Boolean)time).booleanValue();
    		}
    	}


    	if ((isTime) &&((assessmentSettings.getTimeLimit().intValue())==0)){
    		String time_err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","timeSelect_error");
    		context.addMessage(null,new FacesMessage(time_err));
    		error=true;
    	}
    	boolean ipErr=false;
    	String ipString = assessmentSettings.getIpAddresses().trim(); 
    	String[]arraysIp=(ipString.split("\n"));
    	//System.out.println("arraysIp.length: "+arraysIp.length);
    	for(int a=0;a<arraysIp.length;a++){
    		String currentString=arraysIp[a];
    		if(!currentString.trim().equals("")){
    			if(a<(arraysIp.length-1))
    				currentString=currentString.substring(0,currentString.length()-1);           
    			if(!s.isIpValid(currentString)){
    				ipErr=true;
    				break;
    			}
    		}

    	}
    	if(ipErr){
    		error=true;
    		String  ip_err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","ip_error");
    		context.addMessage(null,new FacesMessage(ip_err));
    	}

    	String unlimitedSubmissions = assessmentSettings.getUnlimitedSubmissions();
    	if (unlimitedSubmissions != null && unlimitedSubmissions.equals(AssessmentAccessControlIfc.LIMITED_SUBMISSIONS.toString())) {
    		String submissionsAllowed = assessmentSettings.getSubmissionsAllowed().trim();
    		try {
    			int submissionAllowed = Integer.parseInt(submissionsAllowed);
    			if (submissionAllowed < 1) {
    				throw new RuntimeException();
    			}
    		}
    		catch (RuntimeException e){
    			error=true;
    			String  submission_err = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","submissions_allowed_error");
    			context.addMessage(null,new FacesMessage(submission_err));
    		}
    	}

    	String scoringType=assessmentSettings.getScoringType();
    	if ((scoringType).equals(EvaluationModelIfc.AVERAGE_SCORE.toString()) && "0".equals(assessmentSettings.getUnlimitedSubmissions())) {
    		try {
    			String submissionsAllowed = assessmentSettings.getSubmissionsAllowed().trim();
    			int submissionAllowed = Integer.parseInt(submissionsAllowed);
    			if (submissionAllowed < 2) {
    				throw new RuntimeException();
    			}
    		}
    		catch (RuntimeException e){
    			error=true;
    			String  submission_err = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","averag_grading_single_submission");
    			context.addMessage(null,new FacesMessage(submission_err));
    		}
    	}

    	//check feedback - if at specific time then time should be defined.
    	if((assessmentSettings.getFeedbackDelivery()).equals("2") && ((assessmentSettings.getFeedbackDateString()==null) || (assessmentSettings.getFeedbackDateString().equals("")))){
    		error=true;
    		String date_err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","date_error");
    		context.addMessage(null,new FacesMessage(date_err));
    	}
    }
    else {
    	if (assessmentSettings.getReleaseTo().equals(AssessmentAccessControl.RELEASE_TO_SELECTED_GROUPS)) {
    		String[] groupsAuthorized = assessmentSettings.getGroupsAuthorized(); //populate groupsAuthorized;
    		if (groupsAuthorized == null || groupsAuthorized.length == 0) {
    			String releaseGroupError = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.GeneralMessages","choose_release_to");
    			context.addMessage(null,new FacesMessage(releaseGroupError));
    			error=true;
    			assessmentSettings.setNoGroupSelectedError(true);
    		}
    		else {
    			assessmentSettings.setNoGroupSelectedError(false);
    		} 
    	}
    }
    
    //Gradebook right now only excep if total score >0 check if total score<=0 then throw error.
    if(assessmentSettings.getToDefaultGradebook() != null && assessmentSettings.getToDefaultGradebook().equals("1"))
	{
 	    if(assessmentBean.getTotalScore()<=0)
		{
 	    	String gb_err=(String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages", "gradebook_exception_min_points");
            context.addMessage(null, new FacesMessage(gb_err));
            error=true;
		}
	}

    //#2b - check if gradebook exist, if so, if assessment title already exists in GB
    GradebookExternalAssessmentService g = null;
    if (integrated){
      g = (GradebookExternalAssessmentService) SpringBeanLocator.getInstance().
            getBean("org.sakaiproject.service.gradebook.GradebookExternalAssessmentService");
    }
    String toGradebook = assessmentSettings.getToDefaultGradebook();
    try{
	if (toGradebook!=null && toGradebook.equals(EvaluationModelIfc.TO_DEFAULT_GRADEBOOK.toString()) &&
	    gbsHelper.isAssignmentDefined(assessmentSettings.getTitle(), g)){
        String gbConflict_err= ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages" , "gbConflict_error");
        context.addMessage(null,new FacesMessage(gbConflict_err));
        error=true;
      }
    }
    catch(Exception e){
      log.warn("external assessment in GB has the same title:"+e.getMessage());
    }
   
    if (!isFromActionSelect) {
    	// To convertFormattedTextToPlaintext for the fields that have been through convertPlaintextToFormattedTextNoHighUnicode
    	assessmentSettings.setTitle(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getTitle()));
    	assessmentSettings.setAuthors(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getAuthors()));
    	assessmentSettings.setFinalPageUrl(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getFinalPageUrl()));
    	assessmentSettings.setBgColor(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getBgColor()));
    	assessmentSettings.setBgImage(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getBgImage()));
    	assessmentSettings.setKeywords(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getKeywords()));
    	assessmentSettings.setObjectives(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getObjectives()));
    	assessmentSettings.setRubrics(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getRubrics()));
    	assessmentSettings.setUsername(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getUsername()));
    	assessmentSettings.setPassword(FormattedText.convertFormattedTextToPlaintext(assessmentSettings.getPassword()));
    }
    
    if (error){
      assessmentSettings.setOutcomePublish("editAssessmentSettings");
      author.setIsErrorInSettings(true);
      return;
    }

    //#3 now u can proceed to save core assessment
    if (!isFromActionSelect) {
    	assessment = s.save(assessmentSettings, true);

    	//unEscape the TextFormat.convertPlaintextToFormattedTextNoHighUnicode in s.save()
    	assessment.setTitle(FormattedText.convertFormattedTextToPlaintext(assessment.getTitle()));
    	assessmentSettings.setAssessment(assessment);
    }
    
    //  we need a publishedUrl, this is the url used by anonymous user
    String releaseTo = assessment.getAssessmentAccessControl().getReleaseTo();
    if (releaseTo != null) {
      // generate an alias to the pub assessment
      String alias = AgentFacade.getAgentString() + (new Date()).getTime();
      assessmentSettings.setAlias(alias);
      //log.info("servletPath=" + extContext.getRequestServletPath());
      String server = ( (javax.servlet.http.HttpServletRequest) extContext.
			      getRequest()).getRequestURL().toString();
      int index = server.indexOf(extContext.getRequestContextPath() + "/"); // "/samigo-app/"
      server = server.substring(0, index);
      //log.info("servletPath=" + server);
      String url = server + extContext.getRequestContextPath();
      assessmentSettings.setPublishedUrl(url + "/servlet/Login?id=" + alias);
    }
   
    //#4 - before going to confirm publishing, check if the title is unique
    PublishedAssessmentService publishedService = new PublishedAssessmentService();
    if ( !publishedService.publishedAssessmentTitleIsUnique(assessmentId,assessmentName)){
      String err=ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","published_assessment_title_not_unique_error");
      context.addMessage(null,new FacesMessage(err));
      assessmentSettings.setOutcomePublish("editAssessmentSettings");
      author.setIsErrorInSettings(true);
      return;
    }
    
    //#4 - regenerate the core assessment list in autor bean again
    // sortString can be of these value:title,releaseTo,dueDate,startDate
    // get the managed bean, author and reset the list.
    // Yes, we need to do that just in case the user change those delivery
    // dates and turning an inactive pub to active pub and then go back to assessment list page
    ArrayList assessmentList = assessmentService.getBasicInfoOfAllActiveAssessments(author.getCoreAssessmentOrderBy(),author.isCoreAscending());
	// get the managed bean, author and set the list
	author.setAssessments(assessmentList);
	
	PublishRepublishNotificationBean publishRepublishNotification = (PublishRepublishNotificationBean) ContextUtil.lookupBean("publishRepublishNotification");
	publishRepublishNotification.setSendNotification(false);
	publishRepublishNotification.setPrePopulateText(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AssessmentSettingsMessages","pre_populate_text_publish"));
	assessmentSettings.setOutcomePublish("saveSettingsAndConfirmPublish"); // finally goto confirm
	SetFromPageAsAuthorSettingsListener setFromPageAsAuthorSettingsListener = new SetFromPageAsAuthorSettingsListener();
	setFromPageAsAuthorSettingsListener.processAction(null);
  }

  public boolean passAuthz(FacesContext context, String ownerId){
    AuthorizationBean authzBean = (AuthorizationBean) ContextUtil.lookupBean("authorization");
    boolean hasPrivilege_any = authzBean.getPublishAnyAssessment();
    boolean hasPrivilege_own0 = authzBean.getPublishOwnAssessment();
    boolean hasPrivilege_own = (hasPrivilege_own0 && isOwner(ownerId));
    boolean hasPrivilege = (hasPrivilege_any || hasPrivilege_own);
    if (!hasPrivilege){
      String err=(String)ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.AuthorMessages",
		     "denied_publish_assessment_error");
      context.addMessage(null,new FacesMessage(err));
    }
    return hasPrivilege;
  }

  public boolean isOwner(String ownerId){
    boolean isOwner = false;
    String agentId = AgentFacade.getAgentString();
    isOwner = agentId.equals(ownerId);
    return isOwner;
  }  

  public void setIsFromActionSelect(boolean isFromActionSelect){
	  this.isFromActionSelect = isFromActionSelect;
  }  
}
